#include "face_detect_panel.hpp"

FaceDetectPanel::FaceDetectPanel(bool id)
    : index(0), is_camera_running(false)
{
  std::cout << "Create FaceDetectPanel instance ..." << std::endl;
  if (id)
    this->initUIs();
}

void FaceDetectPanel::initUIs()
{
  this->image_label = new QLabel();
  image_label->setAlignment(Qt::AlignCenter);                              // 当前行中心显示
  image_label->setStyleSheet("background-color: rgb(0, 0, 0);color:red;"); // CSS 样式
  image_label->setPixmap(mat_transfer(QDir::homePath().toStdString() + "/opencv_tutorial_data/images/llk_tpl.png"));
  image_label->setFixedSize(800, 600);

  this->weight_file_edit = new QLineEdit();
  this->weight_file_edit->setEnabled(false);
  this->config_file_edit = new QLineEdit();
  this->config_file_edit->setEnabled(false);

  this->data_file_edit = new QLineEdit();
  this->data_file_edit->setEnabled(false);

  this->show_fps_chk = new QCheckBox("FPS");
  this->show_fps_chk->setChecked(true);
  this->show_score_chk = new QCheckBox("置信度");
  this->show_score_chk->setChecked(true);

  this->score_spinbox = new QDoubleSpinBox();
  this->score_spinbox->setRange(0, 1);
  this->score_spinbox->setValue(0.25);

  this->img_file_rbt = new QRadioButton("图像文件");
  this->video_file_rbt = new QRadioButton("视频文件");
  this->vision_file_rbt = new QRadioButton("摄像头");

  this->camera_combo = new QComboBox(this);
  updateCameraList();

  this->disconnect_camera_btn = new QPushButton("断开摄像头");
  this->disconnect_camera_btn->setEnabled(false);

  this->setLayout(ORIGIN_BOX());

  connect(camera_combo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &FaceDetectPanel::onCameraSelected);
  connect(disconnect_camera_btn, &QPushButton::clicked, this, &FaceDetectPanel::DISCONNECT_CAMERA);
  connect(show_fps_chk, &QCheckBox::stateChanged, this, &FaceDetectPanel::dialog_box);
}

QFont FaceDetectPanel::set_font()
{
  QFont font("Arial", 8, QFont::Bold);
  return font;
}

QVBoxLayout *FaceDetectPanel::ORIGIN_BOX()
{
  QVBoxLayout *vbox = new QVBoxLayout();

  vbox->addWidget(PANEL_1_());
  vbox->addWidget(PANEL_2_());
  vbox->addWidget(PANEL_3_());
  vbox->addWidget(PANEL_4_());
  vbox->addWidget(image_label);

  vbox->addStretch(1);
  // 返回配置好的布局
  return vbox;
}

QPixmap FaceDetectPanel::mat_transfer(const std::string &fileName)
{
  cv::Mat bgr = cv::imread(fileName);
  if (bgr.empty())
  {
    std::cerr << "Error loading image: " << fileName << std::endl;
    return QPixmap();
  }

  std::cout << "width:" << bgr.cols << " height:" << bgr.rows << std::endl;
  cv::Mat image;
  cv::cvtColor(bgr, image, cv::COLOR_BGR2RGB);
  QImage img = QImage(image.data, image.cols, image.rows, image.step, QImage::Format_RGB888);
  img = img.scaled(QSize(800, 500), Qt::KeepAspectRatio);
  std::cout << "width:" << img.width() << " height:" << img.height() << std::endl;

  QPixmap pixmap;
  pixmap = pixmap.fromImage(img);

  return pixmap;
}

QWidget *FaceDetectPanel::PANEL_1_()
{
  QWidget *panel = new QGroupBox("数据");
  QHBoxLayout *hbox = new QHBoxLayout();

  img_file_rbt->setChecked(true);
  hbox->addWidget(img_file_rbt);
  hbox->addWidget(video_file_rbt);
  hbox->addWidget(vision_file_rbt);
  hbox->addWidget(camera_combo);
  hbox->addWidget(data_file_edit);
  hbox->addWidget(SELECT_FILE_BUTTON());

  panel->setLayout(hbox);

  return panel;
}

QWidget *FaceDetectPanel::PANEL_2_()
{
  QWidget *panel = new QGroupBox("模型");
  QHBoxLayout *hbox = new QHBoxLayout();
  hbox->addWidget(new QLabel("权重文件:"));
  hbox->addWidget(this->weight_file_edit);
  hbox->addWidget(this->SELECT_WEIGHT_FILE_BUTTON());
  hbox->addWidget(new QLabel("模型文件:"));
  hbox->addWidget(this->config_file_edit);
  hbox->addWidget(this->SELECT_CONFIG_FILE_BUTTON());

  panel->setLayout(hbox);

  return panel;
}

QWidget *FaceDetectPanel::PANEL_3_()
{
  QWidget *panel = new QGroupBox("显示");
  QHBoxLayout *hbox = new QHBoxLayout();

  hbox->addWidget(show_fps_chk);
  hbox->addWidget(show_score_chk);
  hbox->addStretch(1);
  hbox->addWidget(new QLabel("得分:"));
  hbox->addWidget(score_spinbox);
  panel->setLayout(hbox);

  return panel;
}

QWidget *FaceDetectPanel::PANEL_4_()
{
  QWidget *panel = new QWidget();
  QHBoxLayout *hbox = new QHBoxLayout();
  hbox->addWidget(disconnect_camera_btn);
  hbox->addStretch(1);
  hbox->addWidget(DO_FACE_DETECT_BUTTON());
  panel->setLayout(hbox);

  return panel;
}

QPushButton *FaceDetectPanel::SELECT_FILE_BUTTON()
{
  auto selectBtn = new QPushButton();
  selectBtn->setText("选择...");
  this->ADD_SELECT_FILE_SLOTS(selectBtn);
  return selectBtn;
}

QPushButton *FaceDetectPanel::SELECT_WEIGHT_FILE_BUTTON()
{
  auto weightBtn = new QPushButton();
  weightBtn->setText("选择...");
  this->ADD_SELECT_WEIGHT_FILE_SLOTS(weightBtn);
  return weightBtn;
}

QPushButton *FaceDetectPanel::SELECT_CONFIG_FILE_BUTTON()
{
  auto configtBtn = new QPushButton();
  configtBtn->setText("选择...");
  this->ADD_SELECT_CONFIG_FILE_SLOTS(configtBtn);
  return configtBtn;
}

QPushButton *FaceDetectPanel::DO_FACE_DETECT_BUTTON()
{
  auto applyBtn = new QPushButton();
  applyBtn->setText("运行");
  this->ADD_DO_FACE_DETECT_SLOTS(applyBtn);
  return applyBtn;
}

void FaceDetectPanel::ADD_SELECT_FILE_SLOTS(QPushButton *selectBtn)
{
  connect(selectBtn, &QPushButton::clicked, this, &FaceDetectPanel::SELECT_FILE);
}

void FaceDetectPanel::ADD_SELECT_WEIGHT_FILE_SLOTS(QPushButton *weightBtn)
{
  connect(weightBtn, &QPushButton::clicked, this, &FaceDetectPanel::SELECT_WEIGHT_FILE);
}

void FaceDetectPanel::ADD_SELECT_CONFIG_FILE_SLOTS(QPushButton *configtBtn)
{
  connect(configtBtn, &QPushButton::clicked, this, &FaceDetectPanel::SELECT_CONFIG_FILE);
}

void FaceDetectPanel::ADD_DO_FACE_DETECT_SLOTS(QPushButton *applyBtn)
{
  connect(applyBtn, &QPushButton::clicked, this, &FaceDetectPanel::DO_FACE_DETECT);
}

void FaceDetectPanel::SELECT_FILE()
{
  std::cout << "SELECT_FILE button clicked ..." << std::endl;
  auto fileName = QFileDialog::getOpenFileName(this, "Open Image", QDir::homePath() + "/opencv_tutorial_data/images", tr("*.jpg *.png *.mp4"));
  if (this->img_file_rbt->isChecked())
  {
    if (fileName.isEmpty())
      return;

    this->data_file_edit->setText(fileName);
    cv::Mat bgr = cv::imread(fileName.toStdString());
    std::cout << "width: " << bgr.cols << " height: " << bgr.rows << std::endl;
    cv::Mat image;
    cv::cvtColor(bgr, image, cv::COLOR_BGR2RGB);
    QImage img = QImage(image.data, image.cols, image.rows, image.step, QImage::Format_RGB888);
    img = img.scaled(QSize(800, 500), Qt::KeepAspectRatio);

    QPixmap pixmap;
    pixmap = pixmap.fromImage(img);
    this->image_label->setPixmap(pixmap);

    this->image_label->setPixmap(mat_transfer(fileName.toStdString()));
    this->image_label->setFixedSize(800, 500);
  }

  if (fileName.isEmpty())
    return;
  
  this->data_file_edit->setText(fileName);
}

void FaceDetectPanel::SELECT_WEIGHT_FILE()
{
  std::cout << "SELECT_WEIGHT_FILE button clicked ..." << std::endl;
  auto fileName = QFileDialog::getOpenFileName(this, "选择模型权重文件", QDir::homePath() + "/opencv_tutorial_data/models/face_detector", tr("Weight(*.pb)"));
  if (fileName.isEmpty())
    return;
  this->weight_file_edit->setText(fileName);
}

void FaceDetectPanel::SELECT_CONFIG_FILE()
{
  std::cout << "SELECT_CONFIG_FILE button clicked ..." << std::endl;
  auto fileName = QFileDialog::getOpenFileName(this, "选择模型配置文件", QDir::homePath() + "/opencv_tutorial_data/models/face_detector", tr("Config(*.pbtxt)"));
  if (fileName.isEmpty())
    return;
  this->config_file_edit->setText(fileName);
}

void FaceDetectPanel::DO_FACE_DETECT()
{
  std::cout << "DO_FACE_DETECT button clicked ..." << std::endl;
  auto weight_file = this->weight_file_edit->text();
  auto config_file = this->config_file_edit->text();
  float t_score = this->score_spinbox->value();
  bool show_fps = this->show_fps_chk->isChecked();
  bool show_score = this->show_score_chk->isChecked();

  if (weight_file.isEmpty() || config_file.isEmpty())
  {
    QMessageBox::warning(this, "警告", "模型或数据未设置");
    return;
  }

  auto ret = QMessageBox::question(this, "选择", "你确定要运行人脸检测吗？", QMessageBox::StandardButtons(QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No));

  if (ret == QMessageBox::StandardButton::No)
    return;

  try
  {
    cv::dnn::Net net = cv::dnn::readNetFromTensorflow(weight_file.toStdString(), config_file.toStdString());
    if (this->img_file_rbt->isChecked())
    {
      cv::Mat frame = cv::imread(this->data_file_edit->text().toStdString());
      process_frame(frame, t_score, show_fps, show_score, net);
    }
    else if (this->video_file_rbt->isChecked())
    {
      std::cout << "video_file_rbt: " << this->data_file_edit->text().toStdString() << std::endl;
      cv::VideoCapture capture(this->data_file_edit->text().toStdString());
      cv::waitKey(1000); // 打开后等待1秒
      cv::Mat frame;
      while (true)
      {
        capture.read(frame);
        if (frame.empty()) break;
        process_frame(frame, t_score, show_fps, show_score, net);
        cv::waitKey(1);
        QCoreApplication::processEvents();
      }
    }
    else if (this->vision_file_rbt->isChecked())
    {
      QString selectedCameraPath = camera_combo->currentData().toString();
      if (!selectedCameraPath.isEmpty())
      {
        cv::VideoCapture capture(selectedCameraPath.toStdString());
        if (!capture.isOpened())
        {
          std::cerr << "错误: 无法打开选定的摄像头。" << std::endl;
          return;
        }
        cv::waitKey(1000); // 打开后等待1秒

        cv::Mat frame;
        const int MAX_RETRY = 5;
        const int RETRY_DELAY = 1000; // 毫秒

        this->disconnect_camera_btn->setEnabled(true);
        this->vision_file_rbt->setEnabled(false);
        is_camera_running = true;

        while (is_camera_running) // 限制为100帧用于测试
        {
          bool frameRead = false;
          for (int retry = 0; retry < MAX_RETRY && !frameRead; ++retry)
          {
            frameRead = capture.read(frame);
            if (!frameRead)
            {
              std::cerr << "警告: 无法从摄像头读取帧。重试 " << retry + 1 << "/" << MAX_RETRY << std::endl;
              cv::waitKey(RETRY_DELAY);
            }
          }

          if (!frameRead)
          {
            std::cerr << "错误: 多次尝试后仍无法从摄像头读取帧。" << std::endl;
            break;
          }

          if (frame.empty())
          {
            std::cerr << "警告: 从摄像头获取到空帧。" << std::endl;
            continue;
          }

          // 这里处理帧...
          process_frame(frame, t_score, show_fps, show_score, net);

          if (cv::waitKey(30) >= 0)
            break; // 每帧之间等待30毫秒

          QCoreApplication::processEvents(); // 处理Qt事件，使UI保持响应
        }

        capture.release();
        this->disconnect_camera_btn->setEnabled(false);
        this->vision_file_rbt->setEnabled(true);
      }
    }
    else
    {
      QMessageBox::warning(this, "警告", "请先选择一个摄像头");
      return;
    }
    // cv::VideoCapture capture(1);
    // if (!capture.isOpened())
    // {
    //   std::cerr << "错误: 无法打开摄像头。" << std::endl;
    //   return;
    // }
  }
  catch (cv::Exception &e)
  {
    std::cerr << "Error: " << e.what() << std::endl;
    return;
  }

  QMessageBox::information(this, "信息", "人脸检测运行成功...");
}

void FaceDetectPanel::DISCONNECT_CAMERA()
{
  std::cout << "Disconnecting camera..." << std::endl;
  is_camera_running = false;
  this->disconnect_camera_btn->setEnabled(false);
  this->vision_file_rbt->setEnabled(true);
  this->image_label->setPixmap(QPixmap());
  this->image_label->setText("摄像头已断开");
}

void FaceDetectPanel::dialog_box()
{
  if (this->show_fps_chk->isChecked())
  {
    std::cout << "FPS is on" << std::endl;
    QMessageBox::information(this, "信息", "打开 FPS 显示...");
  }
  else if (!this->show_fps_chk->isChecked())
    std::cout << "FPS is off" << std::endl;
}

void FaceDetectPanel::process_frame(cv::Mat &frame, float t_score, bool sfps, bool sScore, cv::dnn::Net &net)
{
  int64 start = cv::getTickCount();
  cv::Mat blob = cv::dnn::blobFromImage(frame, 1.0, cv::Size(300, 300), cv::Scalar(104, 177, 123), false, false);

  net.setInput(blob);
  cv::Mat probs = net.forward();

  // 1x1xNx7
  cv::Mat detectMat(probs.size[2], probs.size[3], CV_32F, probs.ptr<float>());
  for (int row = 0; row < detectMat.rows; row++)
  {
    float conf = detectMat.at<float>(row, 2);
    if (conf > t_score)
    {
      float x1 = detectMat.at<float>(row, 3) * frame.cols;
      float y1 = detectMat.at<float>(row, 4) * frame.rows;
      float x2 = detectMat.at<float>(row, 5) * frame.cols;
      float y2 = detectMat.at<float>(row, 6) * frame.rows;
      cv::Rect box(x1, y1, x2 - x1, y2 - y1);
      cv::rectangle(frame, box, cv::Scalar(0, 0, 255), 2, 8);
      if (sScore)
      {
        putText(frame, cv::format("%.2f", conf), cv::Point(x1, y1 - 10), cv::FONT_HERSHEY_PLAIN, 2.0, cv::Scalar(255, 0, 0), 2, 8);
      }
    }
  }
  if (sfps)
  {
    float t = (cv::getTickCount() - start) / static_cast<float>(cv::getTickFrequency());
    putText(frame, cv::format("FPS: %.2f", 1.0 / t), cv::Point(20, 40), cv::FONT_HERSHEY_PLAIN, 2.0, cv::Scalar(255, 0, 0), 2, 8);
  }

  cv::Mat image;
  cv::cvtColor(frame, image, cv::COLOR_BGR2RGB);
  QImage img = QImage(image.data, image.cols, image.rows, image.step, QImage::Format_RGB888);
  img = img.scaled(QSize(800, 500), Qt::KeepAspectRatio);

  QPixmap pixmap;
  pixmap = pixmap.fromImage(img);
  this->image_label->setPixmap(pixmap);
}

void FaceDetectPanel::listAvailableCameras()
{
  QString cameraList = "可用的摄像头:\n\n";

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
  const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
  if (cameras.isEmpty())
  {
    cameraList = "没有找到可用的摄像头";
  }
  else
  {
    for (int i = 0; i < cameras.size(); ++i)
    {
      cameraList += QString("%1. %2\n").arg(i + 1).arg(cameras[i].description());
    }
  }
#else
  const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
  if (cameras.isEmpty())
  {
    cameraList = "没有找到可用的摄像头";
  }
  else
  {
    for (int i = 0; i < cameras.size(); ++i)
    {
      cameraList += QString("%1. %2\n").arg(i + 1).arg(cameras[i].description());
    }
  }
#endif

  QMessageBox::information(this, "摄像头信息", cameraList);
}

void FaceDetectPanel::onCameraSelected(int index)
{
  if (index >= 0)
  {
    const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
    if (index < cameras.size())
    {
      // 这里可以添加切换摄像头的逻辑
      qDebug() << "Selected camera:" << cameras[index].description();
    }
  }
}

void FaceDetectPanel::updateCameraList()
{
  // 更新相机列表
  qDebug() << "Updating camera list...";
  camera_combo->clear();

  QMap<QString, QString> deviceMap;

  // 首先使用v4l2-ctl获取系统设备信息
  QProcess process;
  process.start("v4l2-ctl", QStringList() << "--list-devices");
  process.waitForFinished();
  QString output = process.readAllStandardOutput();
  QStringList devices = output.split(QRegExp("\n\n"), Qt::SkipEmptyParts);
  for (const auto &device : devices)
  {
    QStringList lines = device.split('\n', Qt::SkipEmptyParts);
    if (lines.size() > 1)
    {
      QString deviceName = lines[0].trimmed();
      QString devicePath = lines[1].trimmed();
      deviceMap[deviceName] = devicePath;
    }
  }

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
  // 获取相机列表
  const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
#else
  const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
#endif

  // 输出检测到的相机数量
  qDebug() << "Number of cameras detected:" << cameras.count();

  // 如果没有检测到相机，输出系统设备信息
  if (cameras.isEmpty())
  {
    qDebug() << "No cameras found. System devices:";
    qDebug() << output;
  }

  // 遍历相机列表，输出相机信息，并将相机名称添加到下拉框中
  for (const auto &cameraInfo : cameras)
  {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QString description = cameraInfo.description();
    QString id = cameraInfo.id();
    qDebug() << "Camera:" << description
             << "Position:" << cameraInfo.position()
             << "Device name:" << id;
#else
    QString description = cameraInfo.description();
    QString id = cameraInfo.deviceName();
    qDebug() << "Camera:" << description
             << "Position:" << cameraInfo.position()
             << "Device name:" << id;
    qDebug() << "  Is default?" << (cameraInfo == QCameraInfo::defaultCamera());
#endif

    // 尝试匹配v4l2-ctl获取的设备路径
    QString devicePath = deviceMap.value(description, id);
    camera_combo->addItem(description, devicePath);
  }

  // 如果没有检测到相机，输出提示信息
  if (camera_combo->count() == 0)
  {
    qDebug() << "No cameras found in Qt!";
    camera_combo->addItem("No cameras available");
  }
}